Using R in hydrology (EGU2017 short course)

These slides and all other course materials can be found at:

github.com/brry/rhydro #slides

To get all the material including the datasets and presentation source code, we recommend to download the whole github course repository.

This is an R Markdown Notebook.
For discussions, please visit the Hydrology in R Facebook group.
Before running the code blocks below, we suggest to get package installation instructions by running:

source("https://raw.githubusercontent.com/brry/rhydro/master/checkpc.R")


Aim and contents of this workshop

We want to:

We can not:

We have prepared:

 

Before we get started, please let us know your current R knowledge level by filling out the short survey at
bit.ly/knowR


top

Report

Good coding practice, report generation (Rstudio, rmarkdown, R notebook)
Daniel Klotz

Introduction

goals

goals

Why I use R

Why I did not use:

equals

equals

Whats great about R:

  library(ggplot2)
  test_data <- mpg
  test_plot <- ggplot(test_data, aes(displ, hwy, colour = class)) + 
    geom_point()
  test_plot

Why I decided to use R:

pipe

pipe

Previously:

  aggregation_function <- function(x) {
    round(mean(x),2)
  }
  mtcars_subset <- subset(mtcars,hp > 100)
  mtcars_aggregated <- aggregate(. ~ cyl, data = mtcars_subset, FUN = aggregation_function)
  car_data1 <- transform(mtcars_aggregated, kpl = mpg*0.4251)
  print(car_data1)

Now:

library(magrittr)
car_data2 <- 
  mtcars %>%
  subset(hp > 100) %>%
  aggregate(. ~ cyl, data = ., FUN = . %>% mean %>% round(2)) %>%
  transform(kpl = mpg %>% multiply_by(0.4251)) %>%
  print

btw: You can integrate other programming languages with ease. Here an example from Yihui Xie for the use of Fortran in Rmarkdown:

  1. Compile Code:
```r
C Fortran test
      subroutine fexp(n, x)
      double precision x
C  output
      integer n, i
C  input value
      do 10 i=1,n
         x=dexp(dcos(dsin(dble(float(i)))))
  10  continue
      return
      end
```
  1. Run Code:
```r
res = .Fortran("fexp", n=100000L, x=0)
str(res)
```

Be happy with the result: > ## List of 2 > ## $ n: int 100000 > ## $ x: num 2.72


Markdown

HTML: HyperText Markdown Language

John Gruber:

John Gruber

Comparison: Markdown vs. Latex Comparison

Rstudio provides cheat-sheets with the most important informations about many of their “favorite” packages & software:

Cheat Sheet

Rmarkdown

In Rstudio:

Rmark1

Rmark2

Rmark2

“Native” Formats:

Much more possible if you adress pandoc directly: pandoc

Information in the text can be automatically updated with the rest of the document: [time for coffee

Examples

Small Websites

Cayman Theme

Cayman Theme

Books (blogdown)

bookdown1 bookdown2

Blogs (hugo)

hugo

Widgets

cran-gauge superzip

Presentations

bookdown1

Apps (Shiny)

shiny shiny2


top

GIS

Using R as GIS (reading a rainfall shapefile + Kriging, sf + leaflet + mapview + OSMscale)
Berry Boessenkool

Shapefiles

Reading shapefiles with maptools::readShapeSpatial and rgdal::readOGR is obsolete.
Instead, use sf::st_read. sf is on CRAN since oct 2016.
Main reaction when using sf: “Wow, that is fast!”
Download the shapefile or better: download the whole github course repository

rain <- sf::st_read("data/PrecBrandenburg/niederschlag.shp")
Reading layer `niederschlag' from data source `D:\Arbeit\2017\rhydro\presentations\data\PrecBrandenburg\niederschlag.shp' using driver `ESRI Shapefile'
converted into: POLYGON
Simple feature collection with 277 features and 1 field
geometry type:  POLYGON
dimension:      XY
bbox:           xmin: 3250175 ymin: 5690642 xmax: 3483631 ymax: 5932731
epsg (SRID):    NA
proj4string:    +proj=tmerc +lat_0=0 +lon_0=15 +k=0.9996 +x_0=3500000 +y_0=0 +ellps=GRS80 +units=m +no_defs

top

Plotting, maps

Static plot:

plot(rain[,1])

Static map:

prj <- sf::st_crs(rain)$proj4string
centroids <- as.data.frame(centroids)
cent_ll <- OSMscale::projectPoints(Y,X, data=centroids, to=OSMscale::pll(), from=prj)
map_static <- OSMscale::pointsMap(y,x, cent_ll, fx=0.08, type="maptoolkit-topo", zoom=6)
Downloading map with extend 11.029332, 14.981464, 51.10418, 53.765423 ...
Done. Now plotting...

Interactive map:

library(leaflet)
cent_ll$info <- paste0(sample(letters,nrow(cent_ll),TRUE), ", ", round(cent_ll$x,2), 
                       ", ", round(cent_ll$y,2))
leaflet(cent_ll) %>% addTiles() %>% addCircleMarkers(lng=~x, lat=~y, popup=~info)

Interactive map of shapefile:

# devtools::install_github("environmentalinformatics-marburg/mapview", ref = "develop")
library(berryFunctions) # classify, seqPal
Paket <U+393C><U+3E31>berryFunctions<U+393C><U+3E32> wurde unter R Version 3.3.3 erstellt
library(mapview)
col <- seqPal(n=100, colors=c("red","yellow","blue"))[classify(rain$P1)$index]
mapview::mapview(rain, col.regions=col)

top

Kriging

Plot original points colored by third dimension:

pcol <- colorRampPalette(c("red","yellow","blue"))(50)
x <- centroids$X # use cent_ll$x for projected data
y <- centroids$Y
berryFunctions::colPoints(x, y, rain$P1, add=FALSE, col=pcol)

Calculate the variogram and fit a semivariance curve

library(geoR)
geoprec <- as.geodata(cbind(x,y,rain$P1))
vario <- variog(geoprec, max.dist=130000) # other maxdist for lat-lon data
fit <- variofit(vario)
plot(vario)
lines(fit)

Determine a useful resolution (keep in mind that computing time rises exponentially with grid size)

# distance to closest other point:
d <- sapply(1:length(x), function(i)
            min(berryFunctions::distance(x[i], y[i], x[-i], y[-i])) )
# for lat-long data use (2017-Apr only available in github version of OSMscale)
# d <- OSMscale::maxEarthDist(y,x, data=cent_ll, fun=min)
hist(d/1000, breaks=20, main="distance to closest gauge [km]")
mean(d/1000) # 8 km

Perform kriging on a grid with that resolution

res <- 1000 # 1 km, since stations are 8 km apart on average
grid <- expand.grid(seq(min(x),max(x),res),
                    seq(min(y),max(y),res))
krico <- krige.control(type.krige="OK", obj.model=fit)
#krobj <- krige.conv(geoprec, loc=grid, krige=krico)
#save(krobj, file="data/krobj.Rdata")
load("data/krobj.Rdata") # line above is too slow for recreation each time

Plot the interpolated values with or an equivalent (see Rclick 4.15) and add contour lines.

par(mar=c(0,3,0,3))
geoR:::image.kriging(krobj, col=pcol)
colPoints(x, y, rain$P1, col=pcol, legargs=list(horiz=F, title="Prec",y1=0.1,x1=0.9))
points(x,y)
plot(rain, col=NA, add=TRUE)


top

Discharge

River discharge time-series visualisation and extreme value statistics (animation + extremeStat)
Berry Boessenkool


top

Hydmod

Hydrological modelling with airGR
Katie Smith


top

EDA

Exploratory Data Analysis including flow duration curve and trend analysis on time-series
Shaun Harrigan


top

Discussion

Please give us feedback at bit.ly/feedbackR

For discussions, please use the Hydrology in R Facebook group.

LS0tDQp0aXRsZTogIlJoeWRybyINCm91dHB1dDoNCiAgaHRtbF9ub3RlYm9vazoNCiAgICBjb2RlX2ZvbGRpbmc6IG5vbmUNCiAgICB0b2M6IHllcw0KICBodG1sX2RvY3VtZW50Og0KICAgIHRvYzogeWVzDQogICAgdG9jX2RlcHRoOiA0DQogICAgdG9jX2Zsb2F0Og0KICAgICAgY29sbGFwc2VkOiBubw0KLS0tDQoNCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQ0KaWYgKGtuaXRyOjo6aXNfaHRtbF9vdXRwdXQoKSkgew0KICBrbml0cjo6b3B0c19jaHVuayRzZXQoZXZhbCA9IEZBTFNFKQ0KfQ0KYGBgDQoNCiMgVXNpbmcgUiBpbiBoeWRyb2xvZ3kgKEVHVTIwMTcgc2hvcnQgY291cnNlKQ0KDQpUaGVzZSBzbGlkZXMgYW5kIGFsbCBvdGhlciBjb3Vyc2UgbWF0ZXJpYWxzIGNhbiBiZSBmb3VuZCBhdDoNCg0KPGZvbnQgc2l6ZT0iNiI+W2dpdGh1Yi5jb20vYnJyeS9yaHlkcm9dKGh0dHBzOi8vZ2l0aHViLmNvbS9icnJ5L3JoeWRybyk8L2ZvbnQ+IA0KPGZvbnQgc2l6ZT0iNCI+I3NsaWRlczwvZm9udD4gICANCg0KVG8gZ2V0IGFsbCB0aGUgbWF0ZXJpYWwgaW5jbHVkaW5nIHRoZSBkYXRhc2V0cyBhbmQgcHJlc2VudGF0aW9uIHNvdXJjZSBjb2RlLCB3ZSByZWNvbW1lbmQgdG8NCltkb3dubG9hZCB0aGUgd2hvbGUgZ2l0aHViIGNvdXJzZSByZXBvc2l0b3J5XShodHRwczovL2dpdGh1Yi5jb20vYnJyeS9yaHlkcm8vYXJjaGl2ZS9tYXN0ZXIuemlwKS4NCg0KVGhpcyBpcyBhbiBbUiBNYXJrZG93biBOb3RlYm9va10oaHR0cDovL3JtYXJrZG93bi5yc3R1ZGlvLmNvbS9yX25vdGVib29rcy5odG1sKS4gIA0KRm9yIGRpc2N1c3Npb25zLCBwbGVhc2UgdmlzaXQgdGhlIA0KW0h5ZHJvbG9neSBpbiBSIEZhY2Vib29rIGdyb3VwXShodHRwczovL3d3dy5mYWNlYm9vay5jb20vZ3JvdXBzLzExMzAyMTQ3NzcxMjM5MDkvKS4gIA0KQmVmb3JlIHJ1bm5pbmcgdGhlIGNvZGUgYmxvY2tzIGJlbG93LCB3ZSBzdWdnZXN0IHRvIGdldCBwYWNrYWdlIGluc3RhbGxhdGlvbiBpbnN0cnVjdGlvbnMgYnkgcnVubmluZzoNCmBgYFINCnNvdXJjZSgiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL2Jycnkvcmh5ZHJvL21hc3Rlci9jaGVja3BjLlIiKQ0KYGBgDQoNClwNCg0KKipBaW0gYW5kIGNvbnRlbnRzIG9mIHRoaXMgd29ya3Nob3AqKg0KDQpXZSB3YW50IHRvOiAgDQoNCiogU2hvdyBvZmYgaG93IGF3ZXNvbWUgUiBpcyBmb3IgaHlkcm9sb2d5IChpdCdzIFItc29tZSFeXikgIA0KKiBDb252aW5jZSB5b3UgdG8gc3RhcnQgb3IgY29udGludWUgdXNpbmcgUiAgDQoqIFByb3ZpZGUgYWxsIHRoZSBjb2RlIGZvciB5b3UgYXMgYSBzdGFydGluZyBwb2ludA0KDQpXZSBjYW4gbm90OiAgDQoNCiogVGVhY2ggeW91IGFjdHVhbCBSIGNvZGluZyAoOTAgbWlucyBpcyB0b28gc2hvcnQgZm9yIGEgdHV0b3JpYWwpDQoNCldlIGhhdmUgcHJlcGFyZWQ6DQoNCiogW0dvb2QgY29kaW5nIHByYWN0aWNlLCByZXBvcnQgZ2VuZXJhdGlvbl0oI3JlcG9ydCkgKFJzdHVkaW8sIGBybWFya2Rvd25gLCBSIG5vdGVib29rKQ0KKiBbVXNpbmcgUiBhcyBHSVNdKCNnaXMpIChyZWFkaW5nIGEgcmFpbmZhbGwgc2hhcGVmaWxlICsgS3JpZ2luZywgYHNmYCArIGBsZWFmbGV0YCArIGBtYXB2aWV3YCArIGBPU01zY2FsZWApDQoqIFtSaXZlciBkaXNjaGFyZ2UgdGltZS1zZXJpZXNdKCNkaXNjaGFyZ2UpIHZpc3VhbGlzYXRpb24gYW5kIGV4dHJlbWUgdmFsdWUgc3RhdGlzdGljcyAoYGFuaW1hdGlvbmAgKyBgZXh0cmVtZVN0YXRgKQ0KKiBbSHlkcm9sb2dpY2FsIG1vZGVsbGluZ10oI2h5ZG1vZCkgd2l0aCBgYWlyR1JgDQoqIFtFeHBsb3JhdG9yeSBEYXRhIEFuYWx5c2lzXSgjZWRhKSBpbmNsdWRpbmcgZmxvdyBkdXJhdGlvbiBjdXJ2ZSBhbmQgdHJlbmQgYW5hbHlzaXMgb24gdGltZS1zZXJpZXMNCg0KXCANCg0KQmVmb3JlIHdlIGdldCBzdGFydGVkLCBwbGVhc2UgbGV0IHVzIGtub3cgeW91ciBjdXJyZW50IFIga25vd2xlZGdlIGxldmVsIGJ5IGZpbGxpbmcgb3V0IHRoZSBzaG9ydCBzdXJ2ZXkgYXQgIA0KPGZvbnQgc2l6ZT0iNiI+W2JpdC5seS9rbm93Ul0oaHR0cHM6Ly9iaXQubHkva25vd1IpPC9mb250PiANCg0KXA0KDQpbdG9wXSgjdG9wKQ0KDQojIFJlcG9ydA0KR29vZCBjb2RpbmcgcHJhY3RpY2UsIFtyZXBvcnQgZ2VuZXJhdGlvbl0oI3JlcG9ydCkgKFJzdHVkaW8sIGBybWFya2Rvd25gLCBSIG5vdGVib29rKSAgDQoqKkRhbmllbCBLbG90eioqDQoNCiMjIEludHJvZHVjdGlvbg0KDQohW2dvYWxzXShkYW5pZWwvaW50cm8vZ29hbHMuanBlZykNCg0KIyMjIFdoeSBJIHVzZSBSDQoNCldoeSBJIGRpZCBub3QgdXNlOiANCg0KIVtlcXVhbHNdKGRhbmllbC9pbnRyby9lcXVhbHMuanBlZykNCg0KDQpXaGF0cyBncmVhdCBhYm91dCBSOiANCmBgYHtyfQ0KICBsaWJyYXJ5KGdncGxvdDIpDQogIHRlc3RfZGF0YSA8LSBtcGcNCiAgdGVzdF9wbG90IDwtIGdncGxvdCh0ZXN0X2RhdGEsIGFlcyhkaXNwbCwgaHd5LCBjb2xvdXIgPSBjbGFzcykpICsgDQogICAgZ2VvbV9wb2ludCgpDQogIHRlc3RfcGxvdA0KYGBgDQoNCldoeSBJIGRlY2lkZWQgdG8gdXNlIFI6IA0KDQohW3BpcGVdKGRhbmllbC9pbnRyby9tYWdyaXR0ci5qcGVnKQ0KDQpQcmV2aW91c2x5Og0KYGBge3J9DQogIGFnZ3JlZ2F0aW9uX2Z1bmN0aW9uIDwtIGZ1bmN0aW9uKHgpIHsNCiAgICByb3VuZChtZWFuKHgpLDIpDQogIH0NCiAgbXRjYXJzX3N1YnNldCA8LSBzdWJzZXQobXRjYXJzLGhwID4gMTAwKQ0KICBtdGNhcnNfYWdncmVnYXRlZCA8LSBhZ2dyZWdhdGUoLiB+IGN5bCwgZGF0YSA9IG10Y2Fyc19zdWJzZXQsIEZVTiA9IGFnZ3JlZ2F0aW9uX2Z1bmN0aW9uKQ0KICBjYXJfZGF0YTEgPC0gdHJhbnNmb3JtKG10Y2Fyc19hZ2dyZWdhdGVkLCBrcGwgPSBtcGcqMC40MjUxKQ0KICBwcmludChjYXJfZGF0YTEpDQpgYGANCg0KTm93Og0KYGBge3J9DQpsaWJyYXJ5KG1hZ3JpdHRyKQ0KY2FyX2RhdGEyIDwtIA0KICBtdGNhcnMgJT4lDQogIHN1YnNldChocCA+IDEwMCkgJT4lDQogIGFnZ3JlZ2F0ZSguIH4gY3lsLCBkYXRhID0gLiwgRlVOID0gLiAlPiUgbWVhbiAlPiUgcm91bmQoMikpICU+JQ0KICB0cmFuc2Zvcm0oa3BsID0gbXBnICU+JSBtdWx0aXBseV9ieSgwLjQyNTEpKSAlPiUNCiAgcHJpbnQNCmBgYA0KDQoNCioqYnR3OioqDQpZb3UgY2FuIGludGVncmF0ZSBvdGhlciBwcm9ncmFtbWluZyBsYW5ndWFnZXMgd2l0aCBlYXNlLiBIZXJlIGFuIGV4YW1wbGUgZnJvbSANCltZaWh1aSBYaWVdKGh0dHBzOi8veWlodWkubmFtZS9rbml0ci9kZW1vL2VuZ2luZXMvKSBmb3IgdGhlIHVzZSBvZiBgRm9ydHJhbmAgDQppbiBSbWFya2Rvd246DQoNCjEuIENvbXBpbGUgQ29kZToNCiAgICBgYGB7ciBjb21wZm9ydCwgZW5naW5lPSdmb3J0cmFuJywgcmVzdWx0cz0naGlkZScsIGV2YWw9RkFMU0V9DQogICAgQyBGb3J0cmFuIHRlc3QNCiAgICAgICAgICBzdWJyb3V0aW5lIGZleHAobiwgeCkNCiAgICAgICAgICBkb3VibGUgcHJlY2lzaW9uIHgNCiAgICBDICBvdXRwdXQNCiAgICAgICAgICBpbnRlZ2VyIG4sIGkNCiAgICBDICBpbnB1dCB2YWx1ZQ0KICAgICAgICAgIGRvIDEwIGk9MSxuDQogICAgICAgICAgICAgeD1kZXhwKGRjb3MoZHNpbihkYmxlKGZsb2F0KGkpKSkpKQ0KICAgICAgMTAgIGNvbnRpbnVlDQogICAgICAgICAgcmV0dXJuDQogICAgICAgICAgZW5kDQogICAgYGBgDQoNCjIuIFJ1biBDb2RlOg0KICAgIGBgYHtyIHRlc3Rmb3J0LCBjb2xsYXBzZT1UUlVFLCBldmFsID0gRkFMU0V9DQogICAgcmVzID0gLkZvcnRyYW4oImZleHAiLCBuPTEwMDAwMEwsIHg9MCkNCiAgICBzdHIocmVzKQ0KICAgIGBgYA0KDQpCZSBoYXBweSB3aXRoIHRoZSByZXN1bHQ6IA0KPiAgICAgYCMjIExpc3Qgb2YgMmANCj4gICAgIGAjIyAgJCBuOiBpbnQgMTAwMDAwYA0KPiAgICAgYCMjICAkIHg6IG51bSAyLjcyYA0KDQotLS0NCg0KIyMgTWFya2Rvd24gDQpIVCoqTSoqTDogSHlwZXJUZXh0ICoqTWFya2Rvd24qKiBMYW5ndWFnZQ0KDQpKb2huIEdydWJlcjoNCg0KWyFbSm9obiBHcnViZXJdKGRhbmllbC9pbnRyby9Kb2huX0dydWJlcl93aWtpLmpwZWcpXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9Kb2huX0dydWJlcikNCg0KDQpDb21wYXJpc29uOiBNYXJrZG93biB2cy4gTGF0ZXggDQpbIVtDb21wYXJpc29uXShkYW5pZWwvaW50cm8veWlodWlfbGF0ZXgtdnMtbWFya2Rvd24ucG5nKV0oaHR0cHM6Ly95b3V0dS5iZS8yeXZXME9fN3hPZykNCg0KDQpSc3R1ZGlvIHByb3ZpZGVzIGNoZWF0LXNoZWV0cyB3aXRoIHRoZSBtb3N0IGltcG9ydGFudCBpbmZvcm1hdGlvbnMgYWJvdXQgbWFueSANCm9mIHRoZWlyICJmYXZvcml0ZSIgcGFja2FnZXMgJiBzb2Z0d2FyZToNCg0KWyFbQ2hlYXQgU2hlZXRdKGRhbmllbC9pbnRyby9SbWFya2Rvd25fY2hlYXRzaGVldC5wbmcpXShodHRwczovL3d3dy5yc3R1ZGlvLmNvbS9yZXNvdXJjZXMvY2hlYXRzaGVldHMvKQ0KDQojIFJtYXJrZG93bg0KSW4gUnN0dWRpbzogDQoNCjxkaXYgYWxpZ249ImNlbnRlciI+DQogIDxpbWcgd2lkdGg9IjYwMHB4IiBzcmM9ImRhbmllbC9yZXBvcnRzL1JtYXJrMS5wbmciIGFsdD0iUm1hcmsxIiAvPg0KPC9kaXY+DQoNCjxkaXYgYWxpZ249ImNlbnRlciI+DQogIDxpbWcgd2lkdGg9IjYwMHB4IiBzcmM9ImRhbmllbC9yZXBvcnRzL1JtYXJrMi5wbmciIGFsdD0iUm1hcmsyIiAvPg0KPC9kaXY+DQoNCjxkaXYgYWxpZ249ImNlbnRlciI+DQogIDxpbWcgd2lkdGg9IjYwMHB4IiBzcmM9ImRhbmllbC9yZXBvcnRzL1JtYXJrMy5wbmciIGFsdD0iUm1hcmsyIiAvPg0KPC9kaXY+DQoNCiJOYXRpdmUiIEZvcm1hdHM6DQoNCi0gW2h0bWxdKGRhbmllbC9zaG93L1JtYXJrLmh0bWwpIA0KLSBbcGRmXShkYW5pZWwvc2hvdy9SbWFyay5wZGYpIA0KLSBbd29yZF0oZGFuaWVsL3Nob3cvUm1hcmsuZG9jcykNCg0KTXVjaCBtb3JlIHBvc3NpYmxlIGlmIHlvdSBhZHJlc3MgcGFuZG9jIGRpcmVjdGx5OiANClshW3BhbmRvY10oZGFuaWVsL3JlcG9ydHMvcGFuZG9jX2RpYWdyYW0uanBnKV0oaHR0cDovL3BhbmRvYy5vcmcvKQ0KDQpJbmZvcm1hdGlvbiBpbiB0aGUgdGV4dCBjYW4gYmUgYXV0b21hdGljYWxseSB1cGRhdGVkIHdpdGggdGhlIHJlc3Qgb2YgdGhlIA0KZG9jdW1lbnQ6DQpbIVt0aW1lIGZvciBjb2ZmZWVdKGRhbmllbC9yZXBvcnRzL2NvZmZlZS5wbmcpDQoNCiMjIyBFeGFtcGxlcw0KDQojIyMjIFNtYWxsIFdlYnNpdGVzDQo8ZGl2IGFsaWduPSJjZW50ZXIiPg0KICA8YSBocmVmID0gImh0dHA6Ly9yc3R1ZGlvLmdpdGh1Yi5pby90dWZ0ZS8iPg0KICAgIDxpbWcgd2lkdGg9IjYwMHB4IiBzcmM9ImRhbmllbC9leGFtcGxlcy9zd190dWZ0ZS5wbmciIGFsdD0iQ2F5bWFuIFRoZW1lIiAvPg0KICA8L2E+DQo8L2Rpdj4NCg0KPGRpdiBhbGlnbj0iY2VudGVyIj4NCiAgPGEgaHJlZiA9ICJodHRwOi8veWl4dWFuLmNvcy5uYW1lL3ByZXR0eWRvYy9jYXltYW4uaHRtbCI+DQogICAgPGltZyB3aWR0aD0iNjAwcHgiIHNyYz0iZGFuaWVsL2V4YW1wbGVzL3N3X2NheW1hbi5wbmciIGFsdD0iQ2F5bWFuIFRoZW1lIiAvPg0KICA8L2E+DQo8L2Rpdj4NCg0KIyMjIyBCb29rcyAoYmxvZ2Rvd24pDQpbIVtib29rZG93bjFdKGRhbmllbC9leGFtcGxlcy9ib29rZG93bl8xLnBuZyldKGh0dHBzOi8vYm9va2Rvd24ub3JnLykNClshW2Jvb2tkb3duMl0oZGFuaWVsL2V4YW1wbGVzL2Jvb2tkb3duXzIucG5nKV0oaHR0cHM6Ly9ib29rZG93bi5vcmcvKQ0KDQojIyMgQmxvZ3MgKGh1Z28pDQpbIVtodWdvXShkYW5pZWwvZXhhbXBsZXMvYmxvZ3NfaHVnbzEucG5nKV0oaHR0cHM6Ly9ib29rZG93bi5vcmcveWlodWkvYmxvZ2Rvd24vKQ0KDQojIyMjIFdpZGdldHMgDQpbIVtjcmFuLWdhdWdlXShkYW5pZWwvZXhhbXBsZXMvd2lkZ2V0c18xLnBuZyldKGh0dHBzOi8vZ2FsbGVyeS5zaGlueWFwcHMuaW8vY3Jhbi1nYXVnZS8pDQpbIVtzdXBlcnppcF0oZGFuaWVsL2V4YW1wbGVzL3dpZGdldHNfMi5wbmcpXShodHRwczovL3NoaW55LnJzdHVkaW8uY29tL2dhbGxlcnkvc3VwZXJ6aXAtZXhhbXBsZS5odG1sKQ0KDQojIyMjIFByZXNlbnRhdGlvbnMNClshW2Jvb2tkb3duMV0oZGFuaWVsL2V4YW1wbGVzL3ByZXNlbnRhdGlvbnNfMS5wbmcpXShodHRwOi8vcm1hcmtkb3duLnJzdHVkaW8uY29tL3JldmVhbGpzX3ByZXNlbnRhdGlvbl9mb3JtYXQuaHRtbCkNCg0KIyMjIyBBcHBzIChTaGlueSkgDQpbIVtzaGlueV0oZGFuaWVsL2V4YW1wbGVzL3NoaW55XzEucG5nKV0oaHR0cHM6Ly9zaGlueS5yc3R1ZGlvLmNvbS8pDQpbIVtzaGlueTJdKGRhbmllbC9leGFtcGxlcy9zaGlueV8yX2dhbGxlcnkucG5nKV0oaHR0cHM6Ly9zaGlueS5yc3R1ZGlvLmNvbS9nYWxsZXJ5L3N1cGVyemlwLWV4YW1wbGUuaHRtbCkNCg0KDQpcDQpbdG9wXSgjdG9wKQ0KDQojIEdJUw0KVXNpbmcgUiBhcyBHSVMgKHJlYWRpbmcgYSByYWluZmFsbCBzaGFwZWZpbGUgKyBLcmlnaW5nLCBgc2ZgICsgYGxlYWZsZXRgICsgYG1hcHZpZXdgICsgYE9TTXNjYWxlYCkgIA0KKipCZXJyeSBCb2Vzc2Vua29vbCoqDQoNCiMjIyBTaGFwZWZpbGVzDQoNClJlYWRpbmcgc2hhcGVmaWxlcyB3aXRoIGBtYXB0b29sczo6cmVhZFNoYXBlU3BhdGlhbGAgYW5kIGByZ2RhbDo6cmVhZE9HUmAgaXMgb2Jzb2xldGUuICANCkluc3RlYWQsIHVzZSBgc2Y6OnN0X3JlYWRgLiBgc2ZgIGlzIG9uIENSQU4gc2luY2Ugb2N0IDIwMTYuICANCk1haW4gcmVhY3Rpb24gd2hlbiB1c2luZyBzZjogIldvdywgdGhhdCBpcyBmYXN0ISIgIA0KW0Rvd25sb2FkIHRoZSBzaGFwZWZpbGVdKGh0dHBzOi8vbWluaGFza2FtYWwuZ2l0aHViLmlvL0Rvd25HaXQvIy9ob21lP3VybD1odHRwczovL2dpdGh1Yi5jb20vYnJyeS9yaHlkcm8vdHJlZS9tYXN0ZXIvcHJlc2VudGF0aW9ucy9kYXRhL1ByZWNCcmFuZGVuYnVyZykgDQpvciBiZXR0ZXI6IFtkb3dubG9hZCB0aGUgd2hvbGUgZ2l0aHViIGNvdXJzZSByZXBvc2l0b3J5XShodHRwczovL2dpdGh1Yi5jb20vYnJyeS9yaHlkcm8vYXJjaGl2ZS9tYXN0ZXIuemlwKQ0KDQpgYGB7cn0NCnJhaW4gPC0gc2Y6OnN0X3JlYWQoImRhdGEvUHJlY0JyYW5kZW5idXJnL25pZWRlcnNjaGxhZy5zaHAiKQ0KY2VudHJvaWRzIDwtIHNmOjpzdF9jZW50cm9pZChyYWluKQ0KY2VudHJvaWRzIDwtIHNmOjpzdF9jb29yZGluYXRlcyhjZW50cm9pZHMpDQpgYGANCg0KW3RvcF0oI3RvcCkNCg0KIyMjIFBsb3R0aW5nLCBtYXBzDQoNClN0YXRpYyBwbG90Og0KYGBge3J9DQpwbG90KHJhaW5bLDFdKQ0KYGBgDQoNClN0YXRpYyBtYXA6DQpgYGB7cn0NCnByaiA8LSBzZjo6c3RfY3JzKHJhaW4pJHByb2o0c3RyaW5nDQpjZW50cm9pZHMgPC0gYXMuZGF0YS5mcmFtZShjZW50cm9pZHMpDQpjZW50X2xsIDwtIE9TTXNjYWxlOjpwcm9qZWN0UG9pbnRzKFksWCwgZGF0YT1jZW50cm9pZHMsIHRvPU9TTXNjYWxlOjpwbGwoKSwgZnJvbT1wcmopDQptYXBfc3RhdGljIDwtIE9TTXNjYWxlOjpwb2ludHNNYXAoeSx4LCBjZW50X2xsLCBmeD0wLjA4LCB0eXBlPSJtYXB0b29sa2l0LXRvcG8iLCB6b29tPTYpDQpgYGANCg0KSW50ZXJhY3RpdmUgbWFwOg0KYGBge3J9DQpsaWJyYXJ5KGxlYWZsZXQpDQpjZW50X2xsJGluZm8gPC0gcGFzdGUwKHNhbXBsZShsZXR0ZXJzLG5yb3coY2VudF9sbCksVFJVRSksICIsICIsIHJvdW5kKGNlbnRfbGwkeCwyKSwgDQogICAgICAgICAgICAgICAgICAgICAgICIsICIsIHJvdW5kKGNlbnRfbGwkeSwyKSkNCmxlYWZsZXQoY2VudF9sbCkgJT4lIGFkZFRpbGVzKCkgJT4lIGFkZENpcmNsZU1hcmtlcnMobG5nPX54LCBsYXQ9fnksIHBvcHVwPX5pbmZvKQ0KYGBgDQoNCkludGVyYWN0aXZlIG1hcCBvZiBzaGFwZWZpbGU6DQpgYGB7cn0NCiMgbWFrZSBzdXJlIHRvIGhhdmUgaW5zdGFsbGVkIHRoZSBkZXZlbG9wbWVudCB2ZXJzaW9uIG9mIG1hcHZpZXc6IA0KICAjIGRldnRvb2xzOjppbnN0YWxsX2dpdGh1YigiZW52aXJvbm1lbnRhbGluZm9ybWF0aWNzLW1hcmJ1cmcvbWFwdmlldyIsIHJlZiA9ICJkZXZlbG9wIikNCmxpYnJhcnkoYmVycnlGdW5jdGlvbnMpICMgY2xhc3NpZnksIHNlcVBhbA0KbGlicmFyeShtYXB2aWV3KQ0KY29sIDwtIHNlcVBhbChuPTEwMCwgY29sb3JzPWMoInJlZCIsInllbGxvdyIsImJsdWUiKSlbY2xhc3NpZnkocmFpbiRQMSkkaW5kZXhdDQptYXB2aWV3OjptYXB2aWV3KHJhaW4sIGNvbC5yZWdpb25zPWNvbCkNCmBgYA0KDQpbdG9wXSgjdG9wKQ0KDQojIyMgS3JpZ2luZw0KDQpQbG90IG9yaWdpbmFsIHBvaW50cyBjb2xvcmVkIGJ5IHRoaXJkIGRpbWVuc2lvbjoNCmBgYHtyfQ0KcGNvbCA8LSBjb2xvclJhbXBQYWxldHRlKGMoInJlZCIsInllbGxvdyIsImJsdWUiKSkoNTApDQp4IDwtIGNlbnRyb2lkcyRYICMgdXNlIGNlbnRfbGwkeCBmb3IgcHJvamVjdGVkIGRhdGENCnkgPC0gY2VudHJvaWRzJFkNCmJlcnJ5RnVuY3Rpb25zOjpjb2xQb2ludHMoeCwgeSwgcmFpbiRQMSwgYWRkPUZBTFNFLCBjb2w9cGNvbCkNCmBgYA0KDQpDYWxjdWxhdGUgdGhlIHZhcmlvZ3JhbSBhbmQgZml0IGEgc2VtaXZhcmlhbmNlIGN1cnZlDQpgYGB7cn0NCmxpYnJhcnkoZ2VvUikNCmdlb3ByZWMgPC0gYXMuZ2VvZGF0YShjYmluZCh4LHkscmFpbiRQMSkpDQp2YXJpbyA8LSB2YXJpb2coZ2VvcHJlYywgbWF4LmRpc3Q9MTMwMDAwKSAjIG90aGVyIG1heGRpc3QgZm9yIGxhdC1sb24gZGF0YQ0KZml0IDwtIHZhcmlvZml0KHZhcmlvKQ0KcGxvdCh2YXJpbykNCmxpbmVzKGZpdCkNCmBgYA0KDQpEZXRlcm1pbmUgYSB1c2VmdWwgcmVzb2x1dGlvbiANCihrZWVwIGluIG1pbmQgdGhhdCBjb21wdXRpbmcgdGltZSByaXNlcyBleHBvbmVudGlhbGx5IHdpdGggZ3JpZCBzaXplKQ0KYGBge3J9DQojIGRpc3RhbmNlIHRvIGNsb3Nlc3Qgb3RoZXIgcG9pbnQ6DQpkIDwtIHNhcHBseSgxOmxlbmd0aCh4KSwgZnVuY3Rpb24oaSkNCiAgICAgICAgICAgIG1pbihiZXJyeUZ1bmN0aW9uczo6ZGlzdGFuY2UoeFtpXSwgeVtpXSwgeFstaV0sIHlbLWldKSkgKQ0KIyBmb3IgbGF0LWxvbmcgZGF0YSB1c2UgKDIwMTctQXByIG9ubHkgYXZhaWxhYmxlIGluIGdpdGh1YiB2ZXJzaW9uIG9mIE9TTXNjYWxlKQ0KIyBkIDwtIE9TTXNjYWxlOjptYXhFYXJ0aERpc3QoeSx4LCBkYXRhPWNlbnRfbGwsIGZ1bj1taW4pDQpoaXN0KGQvMTAwMCwgYnJlYWtzPTIwLCBtYWluPSJkaXN0YW5jZSB0byBjbG9zZXN0IGdhdWdlIFtrbV0iKQ0KbWVhbihkLzEwMDApICMgOCBrbQ0KYGBgDQoNClBlcmZvcm0ga3JpZ2luZyBvbiBhIGdyaWQgd2l0aCB0aGF0IHJlc29sdXRpb24gDQpgYGB7cn0NCnJlcyA8LSAxMDAwICMgMSBrbSwgc2luY2Ugc3RhdGlvbnMgYXJlIDgga20gYXBhcnQgb24gYXZlcmFnZQ0KZ3JpZCA8LSBleHBhbmQuZ3JpZChzZXEobWluKHgpLG1heCh4KSxyZXMpLA0KICAgICAgICAgICAgICAgICAgICBzZXEobWluKHkpLG1heCh5KSxyZXMpKQ0Ka3JpY28gPC0ga3JpZ2UuY29udHJvbCh0eXBlLmtyaWdlPSJPSyIsIG9iai5tb2RlbD1maXQpDQoja3JvYmogPC0ga3JpZ2UuY29udihnZW9wcmVjLCBsb2M9Z3JpZCwga3JpZ2U9a3JpY28pDQojc2F2ZShrcm9iaiwgZmlsZT0iZGF0YS9rcm9iai5SZGF0YSIpDQpsb2FkKCJkYXRhL2tyb2JqLlJkYXRhIikgIyBsaW5lIGFib3ZlIGlzIHRvbyBzbG93IGZvciByZWNyZWF0aW9uIGVhY2ggdGltZQ0KYGBgDQoNClBsb3QgdGhlIGludGVycG9sYXRlZCB2YWx1ZXMgd2l0aCBccmNvZGV7aW1hZ2V9IG9yIGFuIGVxdWl2YWxlbnQgDQooc2VlIFtSY2xpY2tdKGh0dHBzOi8vZ2l0aHViLmNvbS9icnJ5L3JjbGljaykgNC4xNSkgYW5kIGFkZCBjb250b3VyIGxpbmVzLg0KYGBge3J9DQpwYXIobWFyPWMoMCwzLDAsMykpDQpnZW9SOjo6aW1hZ2Uua3JpZ2luZyhrcm9iaiwgY29sPXBjb2wpDQpjb2xQb2ludHMoeCwgeSwgcmFpbiRQMSwgY29sPXBjb2wsIGxlZ2FyZ3M9bGlzdChob3Jpej1GLCB0aXRsZT0iUHJlYyIseTE9MC4xLHgxPTAuOSkpDQpwb2ludHMoeCx5KQ0KcGxvdChyYWluLCBjb2w9TkEsIGFkZD1UUlVFKQ0KYGBgDQoNClwNClt0b3BdKCN0b3ApDQoNCiMgRGlzY2hhcmdlDQpSaXZlciBkaXNjaGFyZ2UgdGltZS1zZXJpZXMgdmlzdWFsaXNhdGlvbiBhbmQgZXh0cmVtZSB2YWx1ZSBzdGF0aXN0aWNzIChgYW5pbWF0aW9uYCArIGBleHRyZW1lU3RhdGApICANCioqQmVycnkgQm9lc3Nlbmtvb2wqKg0KDQpcDQpbdG9wXSgjdG9wKQ0KDQojIEh5ZG1vZA0KSHlkcm9sb2dpY2FsIG1vZGVsbGluZyB3aXRoIGBhaXJHUmAgICANCioqS2F0aWUgU21pdGgqKg0KDQpcDQpbdG9wXSgjdG9wKQ0KDQojIEVEQQ0KRXhwbG9yYXRvcnkgRGF0YSBBbmFseXNpcyBpbmNsdWRpbmcgZmxvdyBkdXJhdGlvbiBjdXJ2ZSBhbmQgdHJlbmQgYW5hbHlzaXMgb24gdGltZS1zZXJpZXMgICANCioqU2hhdW4gSGFycmlnYW4qKg0KDQpcDQpbdG9wXSgjdG9wKQ0KDQoNCiMgRGlzY3Vzc2lvbg0KDQpQbGVhc2UgZ2l2ZSB1cyBmZWVkYmFjayBhdA0KPGZvbnQgc2l6ZT0iNiI+W2JpdC5seS9mZWVkYmFja1JdKGh0dHBzOi8vYml0Lmx5L2ZlZWRiYWNrUik8L2ZvbnQ+IA0KDQpGb3IgZGlzY3Vzc2lvbnMsIHBsZWFzZSB1c2UgdGhlIA0KW0h5ZHJvbG9neSBpbiBSIEZhY2Vib29rIGdyb3VwXShodHRwczovL3d3dy5mYWNlYm9vay5jb20vZ3JvdXBzLzExMzAyMTQ3NzcxMjM5MDkvKS4gIA0KDQo=